package com.school.service;

import com.school.entity.Dormitory;
import com.school.entity.Student;
import com.school.dao.DormitoryDao;
import com.school.dao.StudentDao;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 宿舍自动分配服务
 */
@Slf4j
@Service
public class DormitoryAllocationService {
    
    @Autowired
    private StudentDao studentDao;
    
    @Autowired
    private DormitoryDao dormitoryDao;
    
    /**
     * 自动分配宿舍
     * 
     * 分配规则：
     * 1. 相同班级、相同性别的学生优先分配在同一个宿舍
     * 2. 如果宿舍未住满，可以分配别的班级学生，但最多3个班级混住
     * 3. 只有未分配宿舍的学生才参与自动分配
     * 4. 根据宿舍的容纳人数进行分配
     * 
     * @return 分配结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> autoAllocateDormitory() {
        long startTime = System.currentTimeMillis();
        log.info("开始自动分配宿舍...");
        
        try {
            // 1. 获取所有未分配宿舍的学生（宿舍号为空或null）
            List<Student> unallocatedStudents = getUnallocatedStudents();
            log.info("找到 {} 个未分配宿舍的学生", unallocatedStudents.size());
            
            if (unallocatedStudents.isEmpty()) {
                return buildResult(true, "没有需要分配宿舍的学生", 0, 0, 0);
            }
            
            // 2. 获取所有可用的宿舍（状态为可用且有空余床位）
            List<Dormitory> availableDormitories = getAvailableDormitories();
            log.info("找到 {} 个可用宿舍", availableDormitories.size());
            
            if (availableDormitories.isEmpty()) {
                return buildResult(false, "没有可用的宿舍，请先添加宿舍", 0, 0, unallocatedStudents.size());
            }
            
            // 3. 按性别分组学生
            Map<String, List<Student>> studentsByGender = unallocatedStudents.stream()
                    .collect(Collectors.groupingBy(Student::getGender));
            
            // 4. 按性别和楼栋分组宿舍
            Map<String, Map<String, List<Dormitory>>> dormitoriesByGenderAndBuilding = 
                    groupDormitoriesByGenderAndBuilding(availableDormitories);
            
            int successCount = 0;
            int failCount = 0;
            
            // 5. 分别为男生和女生分配宿舍
            for (Map.Entry<String, List<Student>> entry : studentsByGender.entrySet()) {
                String gender = entry.getKey();
                List<Student> students = entry.getValue();
                
                log.info("开始为 {} 个{}生分配宿舍", students.size(), gender);
                
                // 获取该性别对应的宿舍（根据楼栋命名规则：A栋-男生、B栋-女生等）
                Map<String, List<Dormitory>> buildingDormitories = dormitoriesByGenderAndBuilding.get(gender);
                
                if (buildingDormitories == null || buildingDormitories.isEmpty()) {
                    log.warn("没有找到适合{}生的宿舍", gender);
                    failCount += students.size();
                    continue;
                }
                
                // 执行分配
                int[] result = allocateStudentsToDormitories(students, buildingDormitories);
                successCount += result[0];
                failCount += result[1];
            }
            
            long endTime = System.currentTimeMillis();
            log.info("宿舍分配完成，耗时 {} ms，成功 {} 人，失败 {} 人", 
                    endTime - startTime, successCount, failCount);
            
            return buildResult(true, "分配完成", successCount, failCount, unallocatedStudents.size());
            
        } catch (Exception e) {
            log.error("自动分配宿舍失败", e);
            throw new RuntimeException("自动分配宿舍失败：" + e.getMessage());
        }
    }
    
    /**
     * 为学生分配宿舍
     * 
     * @param students 待分配的学生列表
     * @param buildingDormitories 可用宿舍（按楼栋分组）
     * @return [成功数, 失败数]
     */
    private int[] allocateStudentsToDormitories(List<Student> students, 
                                                  Map<String, List<Dormitory>> buildingDormitories) {
        int successCount = 0;
        int failCount = 0;
        
        // 按班级分组学生
        Map<String, List<Student>> studentsByClass = students.stream()
                .collect(Collectors.groupingBy(Student::getClassName));
        
        // 为每个班级的学生分配宿舍
        for (Map.Entry<String, List<Student>> classEntry : studentsByClass.entrySet()) {
            String className = classEntry.getKey();
            List<Student> classStudents = classEntry.getValue();
            
            log.info("为班级 {} 的 {} 个学生分配宿舍", className, classStudents.size());
            
            // 尝试为该班级学生分配宿舍
            int[] result = allocateClassStudents(classStudents, buildingDormitories, className);
            successCount += result[0];
            failCount += result[1];
        }
        
        return new int[]{successCount, failCount};
    }
    
    /**
     * 为同一个班级的学生分配宿舍
     */
    private int[] allocateClassStudents(List<Student> students, 
                                         Map<String, List<Dormitory>> buildingDormitories,
                                         String className) {
        int successCount = 0;
        int failCount = 0;
        
        // 遍历所有楼栋的宿舍
        for (Map.Entry<String, List<Dormitory>> buildingEntry : buildingDormitories.entrySet()) {
            String building = buildingEntry.getKey();
            List<Dormitory> dormitories = buildingEntry.getValue();
            
            // 按空余床位数降序排序（优先分配床位多的宿舍）
            dormitories.sort((d1, d2) -> Integer.compare(d2.getAvailable(), d1.getAvailable()));
            
            for (Dormitory dormitory : dormitories) {
                if (students.isEmpty()) {
                    break;
                }
                
                // 检查宿舍是否还有空余床位
                int available = dormitory.getAvailable();
                if (available <= 0) {
                    continue;
                }
                
                // 获取该宿舍当前的学生
                List<Student> currentStudents = getStudentsByDormitory(building, dormitory.getRoomNumber());
                
                // 获取该宿舍当前有哪些班级
                Set<String> currentClasses = currentStudents.stream()
                        .map(Student::getClassName)
                        .collect(Collectors.toSet());
                
                // 如果是空宿舍或者已有该班级，可以直接分配
                if (currentClasses.isEmpty() || currentClasses.contains(className)) {
                    int allocateCount = Math.min(available, students.size());
                    
                    for (int i = 0; i < allocateCount; i++) {
                        Student student = students.remove(0);
                        if (assignStudentToDormitory(student, building, dormitory.getRoomNumber())) {
                            successCount++;
                        } else {
                            failCount++;
                        }
                    }
                    
                    // 更新宿舍已入住人数
                    dormitory.setOccupied(dormitory.getOccupied() + allocateCount);
                    updateDormitoryOccupied(dormitory);
                    
                } 
                // 如果宿舍已有其他班级，但班级数量少于3个，可以混住
                else if (currentClasses.size() < 3) {
                    int allocateCount = Math.min(available, students.size());
                    
                    for (int i = 0; i < allocateCount; i++) {
                        Student student = students.remove(0);
                        if (assignStudentToDormitory(student, building, dormitory.getRoomNumber())) {
                            successCount++;
                        } else {
                            failCount++;
                        }
                    }
                    
                    // 更新宿舍已入住人数
                    dormitory.setOccupied(dormitory.getOccupied() + allocateCount);
                    updateDormitoryOccupied(dormitory);
                }
            }
        }
        
        // 如果还有学生未分配，标记为失败
        failCount += students.size();
        
        return new int[]{successCount, failCount};
    }
    
    /**
     * 将学生分配到指定宿舍
     */
    private boolean assignStudentToDormitory(Student student, String building, String roomNumber) {
        try {
            student.setBuilding(building);
            student.setDormitory(roomNumber);
            student.setStatus("已入住");
            
            // 分配床位号（获取该宿舍当前最大床位号+1）
            int nextBedNumber = getNextBedNumber(building, roomNumber);
            student.setBedNumber(String.valueOf(nextBedNumber));
            
            studentDao.update(student);
            log.debug("成功分配学生 {} 到 {} {}", student.getName(), building, roomNumber);
            return true;
        } catch (Exception e) {
            log.error("分配学生 {} 到宿舍失败", student.getName(), e);
            return false;
        }
    }
    
    /**
     * 获取下一个可用的床位号
     */
    private int getNextBedNumber(String building, String roomNumber) {
        List<Student> currentStudents = getStudentsByDormitory(building, roomNumber);
        if (currentStudents.isEmpty()) {
            return 1;
        }
        
        // 找到最大的床位号
        int maxBedNumber = currentStudents.stream()
                .map(Student::getBedNumber)
                .filter(bed -> bed != null && !bed.isEmpty())
                .mapToInt(bed -> {
                    try {
                        return Integer.parseInt(bed);
                    } catch (NumberFormatException e) {
                        return 0;
                    }
                })
                .max()
                .orElse(0);
        
        return maxBedNumber + 1;
    }
    
    /**
     * 获取指定宿舍的所有学生
     */
    private List<Student> getStudentsByDormitory(String building, String roomNumber) {
        return studentDao.findByDormitory(building, roomNumber);
    }
    
    /**
     * 更新宿舍已入住人数
     */
    private void updateDormitoryOccupied(Dormitory dormitory) {
        dormitoryDao.updateOccupied(dormitory.getId(), dormitory.getOccupied());
    }
    
    /**
     * 获取所有未分配宿舍的学生
     */
    private List<Student> getUnallocatedStudents() {
        return studentDao.findUnallocatedStudents();
    }
    
    /**
     * 获取所有可用的宿舍
     */
    private List<Dormitory> getAvailableDormitories() {
        return dormitoryDao.findAvailableDormitories();
    }
    
    /**
     * 按性别和楼栋分组宿舍
     * 
     * 分组规则（根据楼栋命名判断）：
     * - A栋、C栋、E栋等（奇数或包含男）-> 男生宿舍
     * - B栋、D栋、F栋等（偶数或包含女）-> 女生宿舍
     */
    private Map<String, Map<String, List<Dormitory>>> groupDormitoriesByGenderAndBuilding(
            List<Dormitory> dormitories) {
        
        Map<String, Map<String, List<Dormitory>>> result = new HashMap<>();
        result.put("男", new HashMap<>());
        result.put("女", new HashMap<>());
        
        for (Dormitory dormitory : dormitories) {
            String building = dormitory.getBuilding();
            String gender = determineGenderByBuilding(building);
            
            result.get(gender).computeIfAbsent(building, k -> new ArrayList<>()).add(dormitory);
        }
        
        return result;
    }
    
    /**
     * 根据楼栋名称判断性别
     * 
     * 规则：
     * - 包含"男"字 -> 男生
     * - 包含"女"字 -> 女生
     * - A、C、E、G等奇数字母 -> 男生
     * - B、D、F、H等偶数字母 -> 女生
     * - 数字：奇数 -> 男生，偶数 -> 女生
     */
    private String determineGenderByBuilding(String building) {
        if (building == null || building.isEmpty()) {
            return "男"; // 默认男生
        }
        
        // 包含"男"或"女"字的直接判断
        if (building.contains("男")) {
            return "男";
        }
        if (building.contains("女")) {
            return "女";
        }
        
        // 提取第一个字母或数字
        char firstChar = building.charAt(0);
        
        // 如果是字母（A-Z）
        if (Character.isLetter(firstChar)) {
            char upperChar = Character.toUpperCase(firstChar);
            // A=1, B=2, C=3... 奇数为男生，偶数为女生
            int position = upperChar - 'A' + 1;
            return position % 2 == 1 ? "男" : "女";
        }
        
        // 如果是数字
        if (Character.isDigit(firstChar)) {
            int num = firstChar - '0';
            return num % 2 == 1 ? "男" : "女";
        }
        
        // 默认返回男生
        return "男";
    }
    
    /**
     * 构建返回结果
     */
    private Map<String, Object> buildResult(boolean success, String message, 
                                            int successCount, int failCount, int totalCount) {
        Map<String, Object> result = new HashMap<>();
        result.put("success", success);
        result.put("message", message);
        result.put("totalCount", totalCount);
        result.put("successCount", successCount);
        result.put("failCount", failCount);
        result.put("successRate", totalCount > 0 ? 
                String.format("%.2f%%", successCount * 100.0 / totalCount) : "0%");
        return result;
    }
}

